

/*
 ---------------------------------------------------------------------------
 Copyright (c) 1998-2006, Brian Gladman, Worcester, UK. All rights reserved.

 LICENSE TERMS

 The free distribution and use of this software in both source and binary
 form is allowed (with or without changes) provided that:

   1. distributions of this source code include the above copyright
      notice, this list of conditions and the following disclaimer;

   2. distributions in binary form include the above copyright
      notice, this list of conditions and the following disclaimer
      in the documentation and/or other associated materials;

   3. the copyright holder's name is not used to endorse products
      built using this software without specific written permission.

 DISCLAIMER

 This software is provided 'as is' with no explicit or implied warranties
 in respect of its properties, including, but not limited to, correctness
 and/or fitness for purpose.
 ---------------------------------------------------------------------------
 Issue Date: 20/12/2007

 My thanks to Colin Sinclair for finding an error and suggesting a number
 of improvements to this code
*/

#include <stdio.h>
#include "../../common/types.h"
#include "inc/mmo.h"
#include "inc/mode_hdr.h"
#include "inc/debug.h"

#if defined(__cplusplus)
extern "C"
{
#endif

#define BLOCK_SIZE      AES_BLOCK_SIZE      /* block length                */
#define BLK_ADR_MASK    (BLOCK_SIZE - 1)    /* mask for 'in block' address */
#define INIT_STATE      10
#define AUTH_STATE      11
#define MSG_STATE       12

#define MAX_NONCE_LEN   12

#define ctr_len(x)      (((*(u8*)(x)) & 0x07) + 1)

#define clr_ctr(x,l)    memset((u8*)(x) + BLOCK_SIZE - (l), 0, (l))

#define set_ctr(x,v)                                \
  {                                                 \
    u8 *_p = (u8*)(x) + BLOCK_SIZE;       \
    length_t _t = (v), _l = ctr_len(x);             \
    do                                              \
    {                                               \
      *--_p = (u8)_t; _t >>= 8;                \
    } while (--_l);                                 \
  }

#if 0
#define inc_ctr(x)                                  \
  {                                                 \
    u8 *_p = (u8*)(x) + BLOCK_SIZE;       \
    length_t _l = ctr_len(x);                       \
    while (_l-- && ++(*--_p) == 0) ;                \
  }
#else
#define inc_ctr(x, m)                               \
  do {                                              \
      u8 *_p = (u8*)(x) + BLOCK_SIZE;     \
      if ((++(*--_p)) == ctx->mod)                  \
            *_p = 0;                                \
  }while(0);
#endif

#ifdef LONG_MESSAGES

#  define mlen_len(x)                     \
    (((x) & 0xff00000000000000ll) ? 7 :     \
     ((x) & 0xffff000000000000ll) ? 6 :     \
     ((x) & 0xffffff0000000000ll) ? 5 :     \
     ((x) & 0xffffffff00000000ll) ? 4 :     \
     ((x) & 0xffffffffff000000ll) ? 3 :     \
     ((x) & 0xffffffffffff0000ll) ? 2 : 1)

#else
#  define mlen_len(x) (((x) & 0xff000000) ? 3 : ((x) & 0xffff0000) ? 2 : 1)
#endif
extern int j;



/*****************************************************************************/

u32 mmo_init_and_key(                 /* initialise mode and set key */
            const unsigned char key[],     /* the key value               */
            unsigned long key_len,         /* and its length in bytes     */
            mmo_ctx ctx[1])                /* the mode context            */
{
    ctx->state = RETURN_GOOD;
    if ((key_len != 16)) {
        ctx->state = MMO_BAD_KEY;
    }

#ifdef KEYED_HASH_FUNCTION_FOR_MSG_AUTH
    /* init the ipad and opad */
    memset(UI8_PTR(ctx->ipad), 0x36, 16);
    memset(UI8_PTR(ctx->opad), 0x5c, 16);

    xor_block_aligned(ctx->key1, key, ctx->ipad);
    xor_block_aligned(ctx->key2, key, ctx->opad);

#else
    memcpy(UI8_PTR(ctx->hash_val), key, BLOCK_SIZE);
#endif
    return RETURN_GOOD;
}


/*****************************************************************************/

u32 mmo_hash(
            unsigned char data[],           /* the message buffer        */
            length_t data_len,              /* and its length in bytes   */
            mmo_ctx ctx[1])                 /* the mode context          */
{
    u32 count = 0;
    u32  cnt = 0;
    u32  b_pos = 0;
    u32  bit_len = 0;

    /* hash0 */
    //memset(UI8_PTR(ctx->hash_val), 0, 16);
    memset(UI8_PTR(ctx->mmo_buf), 0, 16);

    while (cnt + BLOCK_SIZE <= data_len) {
        /* g() function */
        aes_encrypt_key(UI8_PTR(ctx->hash_val), BLOCK_SIZE, ctx->aes);

        memcpy(UI8_PTR(ctx->mmo_buf), data+cnt, BLOCK_SIZE);

        /* E(hash0, mi) */
        aes_encrypt(UI8_PTR(ctx->mmo_buf), UI8_PTR(ctx->hash_val), ctx->aes);

        /* E(hash0, mi) XOR mi */
        xor_block_aligned(ctx->hash_val, ctx->hash_val, ctx->mmo_buf);

        printf("HASH(%d) :\n", count++);
        for (j=0;j<16;j++) {
            printf("%x ", UI8_PTR(ctx->hash_val)[j]);
        }
        printf("\n");

        cnt += BLOCK_SIZE;
    }

    /* g(hash(n-1)) function */
    aes_encrypt_key(UI8_PTR(ctx->hash_val), BLOCK_SIZE, ctx->aes);

    memset(UI8_PTR(ctx->mmo_buf), 0, BLOCK_SIZE);
    memcpy(UI8_PTR(ctx->mmo_buf), data+cnt, data_len-cnt);
    UI8_PTR(ctx->mmo_buf)[data_len-cnt] = 0x80;
    if (data_len - cnt < BLOCK_SIZE - 2) {
        bit_len = data_len * 8;
        UI8_PTR(ctx->mmo_buf)[BLOCK_SIZE-2] = (bit_len & 0xff00) >> 8;
        UI8_PTR(ctx->mmo_buf)[BLOCK_SIZE-1] = bit_len & 0x00ff;


        /* calc the hash(n) */
        aes_encrypt(UI8_PTR(ctx->mmo_buf), UI8_PTR(ctx->hash_val), ctx->aes);
        xor_block_aligned(ctx->hash_val, ctx->hash_val, ctx->mmo_buf);

        printf("HASH(%d) :\n", count++);
        for (j=0;j<16;j++) {
            printf("%x ", UI8_PTR(ctx->hash_val)[j]);
        }
        printf("\n");
    }
    else {
        /* case in BLOCK_SIZE - 1, or BLOCK_SIZE - 2 bytes */

        /* calc the hash(n-1) */
        aes_encrypt(UI8_PTR(ctx->mmo_buf), UI8_PTR(ctx->hash_val), ctx->aes);
        xor_block_aligned(ctx->hash_val, ctx->hash_val, ctx->mmo_buf);

        memset(UI8_PTR(ctx->mmo_buf), 0, BLOCK_SIZE);
        bit_len = data_len * 8;
        UI8_PTR(ctx->mmo_buf)[BLOCK_SIZE-2] = (bit_len & 0xff00) >> 8;
        UI8_PTR(ctx->mmo_buf)[BLOCK_SIZE-1] = bit_len & 0x00ff;

        aes_encrypt_key(UI8_PTR(ctx->hash_val), BLOCK_SIZE, ctx->aes);

        aes_encrypt(UI8_PTR(ctx->mmo_buf), UI8_PTR(ctx->hash_val), ctx->aes);
        xor_block_aligned(ctx->hash_val, ctx->hash_val, ctx->mmo_buf);

        printf("HASH(%d) :\n", count++);
        for (j=0;j<16;j++) {
            printf("%x ", UI8_PTR(ctx->hash_val)[j]);
        }
        printf("\n");

    }


    return RETURN_GOOD;
}

/*****************************************************************************/

u32 mmo_encrypt_message(              /* encrypt an entire message */
            length_t msg_len,              /* and its length in bytes   */
            unsigned char msg[],           /* the message buffer        */
            mmo_ctx ctx[1],
            u8 result[])                /* the mode context          */
{
#ifdef KEYED_HASH_FUNCTION_FOR_MSG_AUTH
    ret_type retVal = RETURN_GOOD;

    do {

        if (msg_len == 0) {
            retVal = MMO_MSG_LENGTH_ERROR;
            break;
        }

        ctx->cnt = 0;
        ctx->txt_acnt = 0;
        ctx->msg_len = msg_len;

        if (msg_len <= BLOCK_SIZE) {
            /* at least 32 bytes */
            ctx->mmo_tempBuf = (u8*)malloc(BLOCK_SIZE * 2 * sizeof(u8));
        } else {
            ctx->mmo_tempBuf = (u8*)malloc((BLOCK_SIZE + msg_len) * sizeof(u8));
        }

        if (!ctx->mmo_tempBuf) {
            retVal = MMO_MALLOC_ERROR;
            break;
        }

        ctx->mmo_tempBufLen = msg_len + BLOCK_SIZE;

        /* generate M1: M1 = key1 || M */
        memcpy(UI8_PTR(ctx->mmo_tempBuf), UI8_PTR(ctx->key1), BLOCK_SIZE);
        memcpy(UI8_PTR(ctx->mmo_tempBuf) + BLOCK_SIZE, msg, msg_len);

        /* hash1 = Hash(M1), result store in ctx->hash_val */
        mmo_hash(ctx->mmo_tempBuf, ctx->mmo_tempBufLen, ctx);

        /* generate M2: M2 = key2 || hash1 */
        memcpy(UI8_PTR(ctx->mmo_tempBuf), UI8_PTR(ctx->key2), BLOCK_SIZE);
        memcpy(UI8_PTR(ctx->mmo_tempBuf) + BLOCK_SIZE, UI8_PTR(ctx->hash_val), BLOCK_SIZE);
        ctx->mmo_tempBufLen = 2 * BLOCK_SIZE;

        /* hash2 = Hash(M2) */
        mmo_hash(ctx->mmo_tempBuf, ctx->mmo_tempBufLen, ctx);

        printf("Result:\n");
        for (j=0;j<16;j++) {
            printf("%x ", UI8_PTR(ctx->hash_val)[j]);
        }
        printf("\n");

        free(ctx->mmo_tempBuf);


    }while(0);

    return retVal;

#else

    mmo_hash(msg, msg_len, ctx);
    memcpy(result, UI8_PTR(ctx->hash_val), BLOCK_SIZE);

    printf("\nResult:\n");
    for (j=0;j<16;j++) {
        printf("%x ", result[j]);
    }
    printf("\n");

    return RETURN_GOOD;

#endif
}

#if defined(__cplusplus)
}
#endif
